using System;

namespace RapidHDL
{
	/// <summary>
	/// Truth Table lookup hashtable based on string inputs.
	/// </summary>
	public class TruthTable
	{

		System.Collections.Hashtable oTruthTable;
		string sDefaultOutput;
		int iInputWidth;
		int iOutputWidth;

        string sUndefinedValidInput;
        string sUndefinedDefaultOutput;
		
		public TruthTable(int piInputWidth, int piOutputWidth, string psName)
		{
			sDefaultOutput = "";
			oTruthTable = new System.Collections.Hashtable();		
			iInputWidth = piInputWidth;
			iOutputWidth = piOutputWidth;
            sUndefinedValidInput = "";
            sUndefinedDefaultOutput = "";
		}

		public void AddTableEntry(string psInputVector, string psOutputVector)
		{
			int iIndex;
			string sInputVector;
			char [] sNewInputVector;
			char cInput;

			if (psInputVector.ToUpper() == "DEFAULT" && psOutputVector.Length == iOutputWidth)
			{
				sDefaultOutput = psOutputVector.ToUpper();
				return;
			}

            if (psInputVector.ToUpper() == "DEFAULT-X*1" && psOutputVector.Length == iOutputWidth)
			{
                sUndefinedValidInput = "1";
                sUndefinedDefaultOutput = psOutputVector.ToUpper();
				return;
			}

            if (psInputVector.ToUpper() == "DEFAULT-X*0" && psOutputVector.Length == iOutputWidth)
			{
                sUndefinedValidInput = "0";
                sUndefinedDefaultOutput = psOutputVector.ToUpper();
				return;
			}

			if (psInputVector.Length != iInputWidth || psOutputVector.Length != iOutputWidth)
			{
				System.Diagnostics.Debug.Fail("Truth Table Size Mismatch");
			}
			
			sInputVector = psInputVector.ToUpper();

			for(iIndex = 0; iIndex < psInputVector.Length; iIndex++)
			{
				cInput = sInputVector[iIndex];
				if (cInput != '1' && cInput != '0' && cInput != '*')
				{
					System.Diagnostics.Debug.Fail("Invalid Truth Table Input");
				}

				if(cInput == '*')
				{
					sNewInputVector = sInputVector.ToCharArray();
					sNewInputVector[iIndex] = '0';
					this.AddTableEntry(sNewInputVector.ToString(),psOutputVector);
					sNewInputVector[iIndex] = '1';
					this.AddTableEntry(sNewInputVector.ToString(),psOutputVector);
				}
			}
			oTruthTable.Add(sInputVector,psOutputVector.ToUpper());
		}

		public string Decode(string psInput)
		{
			string sDefault = "";
			if (oTruthTable.ContainsKey(psInput))
				return (string)oTruthTable[psInput];
			else
			{
				if (psInput.IndexOf("x") != -1 || psInput.IndexOf("X") != -1)
                {
                    // is there a default for undefined with at least 1 high input
                    if (sUndefinedValidInput == "1")
                    {
                        if (psInput.IndexOf("1") != -1)
                            return sUndefinedDefaultOutput;
                    }

                    // is there a default for undefined with at least 1 low input
                    if (sUndefinedValidInput == "0")
                    {
                        if (psInput.IndexOf("0") != -1)
                            return sUndefinedDefaultOutput;
                    }
                    return sDefault.PadRight(iOutputWidth, 'x');
                }
                else
                {
                    if (sDefaultOutput == "")
                        return sDefault.PadRight(iOutputWidth, 'x');
                    return sDefaultOutput;
                }
			}
		}

		public System.Collections.Hashtable TruthTableDefinition
		{
			get{return oTruthTable;}
		}

		public string DefaultOutput
		{
			get{return sDefaultOutput;}
		}
	}
}
